/**
 * @author Tobias Boehm
 *
 * 25.03.2011
 */
package org.anddev.andengine.extension.tbsengine.match.gamestate;

import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.UUID;
import java.util.Vector;

import org.anddev.andengine.extension.tbsengine.faction.AbsBaseFaction;
import org.anddev.andengine.extension.tbsengine.match.AbsClientMatch;
import org.anddev.andengine.extension.tbsengine.match.AbsMatch;
import org.anddev.andengine.extension.tbsengine.match.AbsServerMatch;
import org.anddev.andengine.extension.tbsengine.match.AbsStep;
import org.anddev.andengine.extension.tbsengine.match.Round;
import org.anddev.andengine.extension.tbsengine.match.Turn;
import org.anddev.andengine.extension.tbsengine.team.Team;
import org.anddev.andengine.util.path.Path.Step;

/**
 * This class represents an abstract global game state
 * of a specific match. It contains extended versions of
 * <code>PlayerGameState</code>s, a reference to its <code>Match</code>
 * and the current diplomatic relationships between the
 * players in form of a map of {@link Team}s. It also handles
 * global match events like the beginning and the end of a
 * {@link Round}, a {@link Turn}, a {@link Step}, or the
 * end of the <code>Match</code> itself.
 * <p>
 * Notice: <br>
 * You should not extend this class but extend the more
 * client specific ({@link AbsClientGlobalGameState}) or
 * server specific ({@link AbsServerGlobalGameState}) version.
 * 
 * 
 * @author Tobias Boehm
 *
 * @param <MatchClass> The concrete implementation of {@link AbsMatch}.
 * @param <PlayerGameStateClass> The concrete implementation of {@link AbsPlayerGameState}.
 * @param <BaseFactionClass> The concrete implementation of {@link AbsBaseFaction}.
 */
public abstract class AbsGlobalGameState<MatchClass extends AbsMatch<PlayerGameStateClass,?,?>,
										 PlayerGameStateClass extends AbsPlayerGameState<BaseFactionClass>,
										 BaseFactionClass extends AbsBaseFaction> {

	/**
	 * A map of currently playing player (IDs)
	 * which map to corresponding player game states.
	 */
	protected Map<UUID,PlayerGameStateClass> mPlayerIDsPlayerGameStates = Collections.synchronizedMap(new HashMap<UUID,PlayerGameStateClass>());
	
	/**
	 * A reference to the corresponding match.
	 */
	protected final MatchClass mMatch;
	
	/**
	 * A map of the teams in this match.
	 */
	protected Map<Integer,Team> mTeams;

	/**
	 * The the player game state which is activ at the moment.
	 */
	protected PlayerGameStateClass mActivePlayerGameState;
	
	
	
	public AbsGlobalGameState(final MatchClass pMatch){
		
		this.mMatch = pMatch;
	}
	
	/**
	 * This method initializes the game state. 
	 * <p> 
	 * Notice: <br>
	 * This method should be called when all other
	 * constructors in the class hierarchy have been
	 * executed to make sure that all member variables
	 * are available to execute {@link #createTeams()}
	 * and {@link #createPlayerIDsGameStatesMap()}
	 * properly.
	 */
	protected void initializeGlobalGameState(){
		
		this.mTeams = this.createTeams();
		
		this.mPlayerIDsPlayerGameStates = this.createPlayerIDsGameStatesMap();
	}
	
	/**
	 * This method should create a map of teams
	 * which are easily accessible through their team IDs.
	 * <p>
	 * You may query the {@link #mMatch} to get enough
	 * information to create those teams.
	 * 
	 * @return A map of TeamIDs -> Teams.
	 */
	protected abstract Map<Integer, Team> createTeams();

	/**
	 * This method should create a map of player IDs and
	 * corresponding <code>PlayerGameStates</code>.
	 * 
	 * @return
	 */
	protected abstract Map<UUID, PlayerGameStateClass> createPlayerIDsGameStatesMap();

	/**
	 * This method starts the match with the
	 * first round and the first turn. 
	 * <p>
	 * Notice:<br>
	 * This method is called by your implementation of
	 * {@link AbsClientMatch} or {@link AbsServerMatch}.
	 * You must not call this method manually.
	 */
	public void startMatch(){
		
		this.startNextRound();
	}
	
	/**
	 * This method starts the next round.
	 */
	protected void startNextRound(){
		
		final Vector<Round<PlayerGameStateClass>> completedRoundsList = this.mMatch.getCompletedRounds();
		
		int nextRoundID = 1;
		
		if(!completedRoundsList.isEmpty()){
			
			nextRoundID += this.mMatch.getCompletedRounds().lastElement().getRoundID();
		}
		
		final Round<PlayerGameStateClass> round = new Round<PlayerGameStateClass>(nextRoundID, this);
		
		this.mMatch.setCurrentRound(round);
		
		round.startRound();
	}


	/**
	 * This method should be called when
	 * the corresponding match is completed.
	 */
	public void onMatchCompleted(){
		
		this.mMatch.completeMatch();
	}
	
	/**
	 * This method should handle the
	 * start of a new round.
	 * 
	 * @param pRound - The round which started some moments ago.
	 */
	public abstract void onRoundStarted(final Round<PlayerGameStateClass> pRound);
	
	/**
	 * This method handles the ending of a round.
	 * 
	 * @param pRound
	 */
	public void onRoundCompleted(final Round<PlayerGameStateClass> pRound){
		
		this.mMatch.moveCurrendRoundToCompletedRounds();
	}
		
	/**
	 * This method should handle the start of a new turn.
	 * 
	 * @param mCurrentTurn
	 */
	public void onTurnStarted(final Turn pCurrentTurn){
		
		this.mActivePlayerGameState = this.mPlayerIDsPlayerGameStates.get(pCurrentTurn.getPlayerGameState());
	}
	
	/**
	 * This method handles the ending of a turn.
	 * 
	 * @param pTurn
	 */
	public void onTurnCompleted(final Turn pTurn){
		
		this.mActivePlayerGameState = null;
		
		this.mMatch.getCurrentRound().startNextTurn();
	}
		
	/**
	 * This method handles the ending of a step.
	 * 
	 * @param pStep
	 */
	public void onStepCompleted(final AbsStep pStep){
		
		this.mMatch.getCurrentRound().getCurrentTurn().addCompletedStep(pStep);
	}
	
	/**
	 * This method returns all player game states
	 * in a sorted set. The game states are sorted
	 * by their game state id.
	 * 
	 * @return the mPlayerIDsPlayerGameStates
	 */
	public SortedSet<PlayerGameStateClass> getPlayerGameStates() {
		
		final TreeSet<PlayerGameStateClass> sortedPlayerGameStates = new TreeSet<PlayerGameStateClass>(this.mPlayerIDsPlayerGameStates.values());
		
		return sortedPlayerGameStates;
	}
	
	/**
	 * This interface is only a flag to
	 * mark a class as game state element.
	 * 	
	 * @author Tobias Boehm
	 */
	public interface IGameStateElement{
		
	}
}